package com.example.dicomviewer;

import android.util.Log;
import android.util.Pair;

import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Tag;
import org.dcm4che3.data.UID;
import org.dcm4che3.data.VR;
import org.dcm4che3.io.DicomEncodingOptions;
import org.dcm4che3.io.DicomInputStream;
import org.dcm4che3.media.DicomDirReader;
import org.dcm4che3.media.DicomDirWriter;
import org.dcm4che3.media.RecordFactory;
import org.dcm4che3.media.RecordType;
import org.dcm4che3.tool.common.FilesetInfo;
import org.dcm4che3.util.UIDUtils;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;

/*
 * 文件夹读取类,主要用来读取文件夹序列的信息
 *
 * 公开类只有Read,用来读取文件夹信息,然后返回一个 Uri字符数组
 * */
public class SortedDicomDirReader {

    final static String TAG = "SortedDicomDirReader";
    final static String dicomdir_filename = "DICOMDIR";

    DicomEncodingOptions encOpts = DicomEncodingOptions.DEFAULT;
    FilesetInfo fsInfo = new FilesetInfo();
    boolean checkDuplicate = false;
    RecordFactory recFact = new RecordFactory();

    DicomDirReader in;
    DicomDirWriter out;

    public ArrayList<String> Read(String filePathName) {
        // filePathName 用以确定文件夹的路径
        try {
            // 初始化目录结构
//            String cachePath = this.getExternalCacheDir().toString();
            // path 用以确定存储DICOMDIR文件的位置(函数中会创建它)
            String path = filePathName + "/DICOMDIR";
//            String filePathName = cachePath + "/NEU-02";
            create(new File(path));
            addReferenceTo(new File(filePathName));
            Log.d(TAG, "Add reference finished");

            // 读取目录
            ArrayList<Pair<Integer, String>> fileNames = new ArrayList<>();
            String[] patIDs = null;
            Attributes patRec = in.findPatientRecord(patIDs);
            while (patRec != null) {
                String[] studyIUIDs = null;
                Attributes studyRec = in.findStudyRecord(patRec, studyIUIDs);
                while (studyRec != null) {
                    String[] seriesIUIDs = null;
                    Attributes seriesRec = in.findSeriesRecord(studyRec, seriesIUIDs);
                    while (seriesRec != null) {
                        String[] sopIUIDs = null;
                        Attributes instRec = in.findLowerInstanceRecord(seriesRec, true, sopIUIDs);
                        while (instRec != null) {
                            String[] fileIDs = instRec.getStrings(Tag.ReferencedFileID);
                            String uri = in.toFile(fileIDs).toURI().toString();
                            Log.d(TAG, "find instence :" + uri);
                            fileNames.add(new Pair(Integer.parseInt(
                                    instRec.getString(Tag.InstanceNumber)), uri));
                            if (sopIUIDs != null && sopIUIDs.length == 1)
                                break;

                            instRec = in.findNextInstanceRecord(instRec, true, sopIUIDs);
                        }
                        if (seriesIUIDs != null && seriesIUIDs.length == 1)
                            break;

                        seriesRec = in.findNextSeriesRecord(seriesRec, seriesIUIDs);
                    }
                    if (studyIUIDs != null && studyIUIDs.length == 1)
                        break;

                    studyRec = in.findNextStudyRecord(studyRec, studyIUIDs);
                }
                if (patIDs != null && patIDs.length == 1)
                    break;

                patRec = in.findNextPatientRecord(patRec, patIDs);
            }

            // 对 pair 数组进行排序
            Collections.sort(fileNames, (o1, o2) -> {
                if (o1.first > o2.first) return 1;
                else if (o1.first < o2.first) return -1;
                return 0;
            });
//            Log.d(TAG, fileNames.toString());
            ArrayList<String> filePaths = new ArrayList<>();
            for (Pair<Integer, String> fileName : fileNames) {
                filePaths.add(fileName.second);
            }
            Log.d(TAG, filePaths.toString());
            return filePaths;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new ArrayList<>();
    }

    protected void create(File file) throws IOException {
        DicomDirWriter.createEmptyDirectory(file,
                UIDUtils.createUIDIfNull(fsInfo.getFilesetUID()),
                fsInfo.getFilesetID(),
                fsInfo.getDescriptorFile(),
                fsInfo.getDescriptorFileCharset());
        in = out = DicomDirWriter.open(file);
        out.setEncodingOptions(encOpts);
    }


    protected int addReferenceTo(File f) throws IOException {
        Log.d(TAG, "add reference to " + f.toPath());
        int n = 0;
        if (f.isDirectory()) {
            for (String s : f.list()) {
                File sonFile = new File(f, s);
                if (sonFile.isDirectory()) {
                    continue;
                }
                n += addReferenceTo(new File(f, s));
            }
            return n;
        }
        // do not add reference to DICOMDIR
        if (f.equals(dicomdir_filename))
            return 0;

        Attributes fmi;
        Attributes dataset;
        DicomInputStream din = null;
        try {
            din = new DicomInputStream(f);
            din.setIncludeBulkData(DicomInputStream.IncludeBulkData.NO);
            fmi = din.readFileMetaInformation();
            dataset = din.readDataset(-1, Tag.PixelData);
        } catch (IOException e) {
            System.out.println("failed to parse image '" + f + "' - " + e.getMessage());
            return 0;
        } finally {
            if (din != null)
                try {
                    din.close();
                } catch (Exception ignore) {
                }
        }
        char prompt = '.';
        if (fmi == null) {
            fmi = dataset.createFileMetaInformation(UID.ImplicitVRLittleEndian);
            prompt = 'F';
        }
        String iuid = fmi.getString(Tag.MediaStorageSOPInstanceUID, null);
        if (iuid == null) {
            System.out.println("skip DICOM file '" + f + "' without SOP Instance UID (0008, 0018)");
            return 0;
        }

        return addRecords(dataset, n, out.toFileIDs(f), prompt, iuid, fmi);
    }

    private int addRecords(Attributes dataset, int num, String[] fileIDs, char prompt, String iuid, Attributes fmi)
            throws IOException {
        String pid = dataset.getString(Tag.PatientID, null);
        String styuid = dataset.getString(Tag.StudyInstanceUID, null);
        String seruid = dataset.getString(Tag.SeriesInstanceUID, null);

        if (styuid != null) {
            if (pid == null) {
                dataset.setString(Tag.PatientID, VR.LO, pid = styuid);
                prompt = prompt == 'F' ? 'P' : 'p';
            }
            Attributes patRec = in.findPatientRecord(pid);
            if (patRec == null) {
                patRec = recFact.createRecord(RecordType.PATIENT, null,
                        dataset, null, null);
                out.addRootDirectoryRecord(patRec);
                num++;
            }
            Attributes studyRec = in.findStudyRecord(patRec, styuid);
            if (studyRec == null) {
                studyRec = recFact.createRecord(RecordType.STUDY, null,
                        dataset, null, null);
                out.addLowerDirectoryRecord(patRec, studyRec);
                num++;
            }

            if (seruid != null) {
                Attributes seriesRec = in.findSeriesRecord(studyRec, seruid);
                if (seriesRec == null) {
                    seriesRec = recFact.createRecord(RecordType.SERIES, null,
                            dataset, null, null);
                    out.addLowerDirectoryRecord(studyRec, seriesRec);
                    num++;
                }

                if (iuid != null) {
                    Attributes instRec;
                    if (checkDuplicate) {
                        instRec = in.findLowerInstanceRecord(seriesRec, false, iuid);
                        if (instRec != null) {
                            System.out.print('-');
                            return 0;
                        }
                    }
                    instRec = recFact.createRecord(dataset, fmi, fileIDs);
                    out.addLowerDirectoryRecord(seriesRec, instRec);
                    num++;
                }
            }
        } else {
            if (iuid != null) {
                if (checkDuplicate) {
                    if (in.findRootInstanceRecord(false, iuid) != null) {
                        System.out.print('-');
                        return 0;
                    }
                }
                Attributes instRec = recFact.createRecord(dataset, fmi, fileIDs);
                out.addRootDirectoryRecord(instRec);
                prompt = prompt == 'F' ? 'R' : 'r';
                num++;
            }
        }
        System.out.print(prompt);
        return num;
    }

    protected void close() {
        if (in != null) {
            try {
                in.close();
            } catch (IOException ignore) {
            }
        }
        in = null;
        out = null;
    }
}
